#import "RNDetailPrintView.h"
#import "NSTreeController+SRAdditions.h"
#import "RNCategory.h"
#import "RNAisle.h"
#import "RNConstants.h"
#import "RNListItem.h"
#import "RNItem.h"
#import "RNDetailHeader.h"

static CGFloat const genericHeight = 17.0f;
static CGFloat const listContainerHeight = 38.0f;
static CGFloat const itemWithNotesHeight = 30.0f;
static CGFloat const endPadding = 3.0f;
static CGFloat const elementPadding = 10.0f;
static CGFloat const verticalTextPadding = 5.0f;
static CGFloat const containerCountWidth = 42.0f;
static CGFloat const itemCountWidth = 10.0f;
static CGFloat const itemCountVerticalPadding = 2.0f;
static CGFloat const checkboxLength = 10.0f;
static CGFloat const checkboxVerticalPadding = 4.0f;
static CGFloat const headerLinePadding = 5.0f;
static CGFloat const headerTextPadding = 10.0f;
static CGFloat const footerTextPadding = 10.0f;

static NSColor* RNDetailPrintViewCheckboxColor;

@interface RNDetailPrintView (RNDetailPrintViewPrivateMethods)
-(CGFloat)printHeightForObject:(id)object;
-(void)drawObject:(id)object inRect:(NSRect)drawRect;
-(void)drawListContainer:(id)listContainer inRect:(NSRect)drawRect;
-(void)drawContainer:(id)container inRect:(NSRect)drawRect;
-(void)drawItem:(id)item inRect:(NSRect)drawRect;
@end

@implementation RNDetailPrintView

@synthesize detailTreeController = detailTreeController_;
@synthesize rootObject = rootObject_;
@synthesize currentPage = currentPage_;
@synthesize pageObjects = pageObjects_;

+(void)initialize {
	RNDetailPrintViewCheckboxColor = [[NSColor blackColor] copy];
}

-(BOOL)isFlipped {
    return YES;
}

-(NSAttributedString *)listContainerAttributedStringForItem:(id)item {
    NSMutableDictionary *attributes = [NSMutableDictionary dictionary];
    [attributes setValue:[NSFont fontWithName:@"Palatino-Bold" size:13.0f] forKey:NSFontAttributeName];
    [attributes setValue:[NSNumber numberWithInteger:NSUnderlineStyleSingle | NSUnderlinePatternSolid] forKey:NSUnderlineStyleAttributeName];
    return [[NSAttributedString alloc] initWithString:[item title] attributes:attributes];
}

-(NSAttributedString *)containerAttributedStringForItem:(id)item {
    NSMutableDictionary *attributes = [NSMutableDictionary dictionaryWithObject:[NSFont fontWithName:@"Palatino" size:12.0f] forKey:NSFontAttributeName];
    return [[NSAttributedString alloc] initWithString:[item title] attributes:attributes];
}

-(NSAttributedString *)containerCountAttributedStringForItem:(id)item {
    NSMutableDictionary *attributes = [NSMutableDictionary dictionary];
    [attributes setValue:[NSFont fontWithName:@"Palatino" size:10.0f] forKey:NSFontAttributeName];
    [attributes setValue:[NSColor colorWithCalibratedWhite:0.5 alpha:1.0] forKey:NSForegroundColorAttributeName];
    return [[NSAttributedString alloc] initWithString:[NSString stringWithFormat:@"%@ %@", [item itemsCount], [[item itemsCount] integerValue] == 1 ? @"item" : @"items"] attributes:attributes];
}

-(NSAttributedString *)itemAttributedStringForItem:(id)item {
    NSMutableDictionary *attributes = [NSMutableDictionary dictionaryWithObject:[NSFont fontWithName:@"Palatino" size:12.0f] forKey:NSFontAttributeName];
    return [[NSAttributedString alloc] initWithString:[item title] attributes:attributes];
}

-(NSAttributedString *)itemNotesAttributedStringForItem:(id)item {
    NSMutableDictionary *attributes = [NSMutableDictionary dictionary];
    [attributes setValue:[NSFont fontWithName:@"Palatino" size:10] forKey:NSFontAttributeName];
    [attributes setValue:[NSColor colorWithCalibratedWhite:0.5 alpha:1.0] forKey:NSForegroundColorAttributeName];
    return [[NSAttributedString alloc] initWithString:[item notes] attributes:attributes];
}

-(NSAttributedString *)itemCountAttributedStringForItem:(id)item {
    if (![item isKindOfClass:[RNListItem class]])
        return nil;
    NSMutableDictionary *attributes = [NSMutableDictionary dictionary];
    [attributes setValue:[NSFont fontWithName:@"Palatino" size:10] forKey:NSFontAttributeName];
    return [[NSAttributedString alloc] initWithString:[NSString stringWithFormat:@"%@", [item count]] attributes:attributes];
}

-(NSAttributedString *)headerAttributedString {
    NSMutableDictionary *attributes = [NSMutableDictionary dictionary];
    [attributes setValue:[NSFont fontWithName:@"Georgia" size:[self currentPage] == 0 ? 24 : 18] forKey:NSFontAttributeName];
    NSString *postfix = [self currentPage] == 0 ? @"" : @" (cont)";
    return [[NSAttributedString alloc] initWithString:[NSString stringWithFormat:@"%@%@", [[self rootObject] title], postfix] attributes:attributes];
}

-(NSAttributedString *)footerAttributedString {
    NSMutableDictionary *attributes = [NSMutableDictionary dictionary];
    [attributes setValue:[NSFont fontWithName:@"Palatino" size:11] forKey:NSFontAttributeName];
    return [[NSAttributedString alloc] initWithString:[NSString stringWithFormat:@"Page %i", [self currentPage] + 1] attributes:attributes];
}

-(void)drawRect:(NSRect)dirtyRect {

    NSRect printRect = [self frame];
    CGFloat currentY = NSMinY(printRect);

    NSArray *objectsInCurrentPage = [[self pageObjects] objectAtIndex:[self currentPage]];
    for (id object in objectsInCurrentPage) {
        CGFloat currentHeight = [self printHeightForObject:object];
        NSRect currentRect = NSMakeRect(NSMinX(printRect), currentY, NSWidth(printRect), currentHeight);
        [self drawObject:object inRect:currentRect];
        currentY += currentHeight;
    }
}

-(void)drawPageBorderWithSize:(NSSize)borderSize {
    NSRect originalFrame = [self frame];
    NSRect pageRect = NSMakeRect(0, 0, borderSize.width, borderSize.height);
    [self setFrame:pageRect];

    NSPrintOperation *printOperation = [NSPrintOperation currentOperation];
    NSPrintInfo *printInfo = [printOperation printInfo];
    CGFloat topMargin = [printInfo topMargin];
    
    NSPoint lineLeftPoint = NSMakePoint([printInfo leftMargin], [printInfo topMargin] - headerLinePadding);
    NSPoint lineRightPoint = NSMakePoint(borderSize.width - [printInfo rightMargin], [printInfo topMargin] - headerLinePadding);
    [self lockFocus];
    [NSBezierPath strokeLineFromPoint:lineLeftPoint toPoint:lineRightPoint];
    
    NSAttributedString *headerString = [self headerAttributedString];
    NSSize headerSize = [headerString size];
    NSRect headerRect = NSMakeRect((borderSize.width / 2) - (headerSize.width / 2), topMargin - headerTextPadding - headerSize.height, headerSize.width, headerSize.height);
    [headerString drawInRect:headerRect];
    
    NSAttributedString *footerString = [self footerAttributedString];
    NSSize footerSize = [footerString size];
    NSRect footerRect = NSMakeRect((borderSize.width / 2) - (footerSize.width / 2), borderSize.height - [printInfo bottomMargin] + footerTextPadding, footerSize.width, footerSize.height);
    [footerString drawInRect:footerRect];
    
    [self unlockFocus];
    [self setFrame:originalFrame];
}

-(void)drawObject:(id)object inRect:(NSRect)drawRect {
    if ([object isKindOfClass:[RNCategory class]])
        [RNCategory showsChildren] ? [self drawListContainer:object inRect:drawRect] : [self drawContainer:object inRect:drawRect];
    else if ([object isKindOfClass:[RNAisle class]])
        [RNAisle showsChildren] ? [self drawListContainer:object inRect:drawRect] : [self drawContainer:object inRect:drawRect];
    else if ([object isKindOfClass:[RNListItem class]] || [object isKindOfClass:[RNItem class]]) 
        [self drawItem:object inRect:drawRect];
}

-(void)drawListContainer:(id)listContainer inRect:(NSRect)drawRect {
    NSAttributedString *title = [self listContainerAttributedStringForItem:listContainer];
    NSSize titleSize = [title size];
    NSRect titleRect = NSMakeRect(NSMinX(drawRect), NSMaxY(drawRect) - verticalTextPadding - titleSize.height, titleSize.width, titleSize.height);
    [title drawInRect:titleRect];
}

-(void)drawContainer:(id)container inRect:(NSRect)drawRect {
    NSAttributedString *countString = [self containerCountAttributedStringForItem:container];
    NSSize countSize = [countString size];
    NSRect countRect = NSMakeRect(NSMinX(drawRect) + endPadding, NSMinY(drawRect) + 2, containerCountWidth, countSize.height);
    [countString drawInRect:countRect];

    NSAttributedString *title = [self containerAttributedStringForItem:container];
    NSSize titleSize = [title size];
    NSRect titleRect = NSMakeRect(NSMaxX(countRect) + elementPadding, NSMinY(drawRect), titleSize.width, titleSize.height);
    [title drawInRect:titleRect];
}

-(void)drawItem:(id)item inRect:(NSRect)drawRect {
    NSAttributedString *countString = [self itemCountAttributedStringForItem:item];
    NSRect countRect = NSZeroRect;
    if (countString) {
        NSRect checkboxRect = NSMakeRect(NSMinX(drawRect) + endPadding, NSMinY(drawRect) + checkboxVerticalPadding, checkboxLength, checkboxLength);
		[RNDetailPrintViewCheckboxColor setStroke];
        [NSBezierPath strokeRect:checkboxRect];
        NSSize countSize = [countString size];
        countRect = NSMakeRect(NSMaxX(checkboxRect) + elementPadding, NSMinY(drawRect) + itemCountVerticalPadding, itemCountWidth, countSize.height);
        [countString drawInRect:countRect];
    }
    NSAttributedString *title = [self itemAttributedStringForItem:item];
    NSSize titleSize = [title size];
	CGFloat minX = fmax(NSMinX(drawRect) + endPadding, NSMaxX(countRect) + elementPadding);
    NSRect titleRect = NSMakeRect(minX, NSMinY(drawRect), titleSize.width, titleSize.height);
    [title drawInRect:titleRect];
	if (![item respondsToSelector:@selector(notes)] || ![item notes])
		return;
	NSAttributedString *notes = [self itemNotesAttributedStringForItem:item];
	NSSize notesSize = [notes size];
	NSRect notesRect = NSMakeRect(minX, NSMaxY(drawRect) - notesSize.height, drawRect.size.width, notesSize.height);
	[notes drawInRect:notesRect];
}

-(BOOL)knowsPageRange:(NSRangePointer)range {        
    NSPrintOperation *printOperation = [NSPrintOperation currentOperation];
    NSPrintInfo *printInfo = [printOperation printInfo];
    NSSize pageSize = [printInfo paperSize];
    pageSize.width -= [printInfo leftMargin] + [printInfo rightMargin];
    pageSize.height -= [printInfo topMargin] + [printInfo bottomMargin];
    CGFloat pageHeight = pageSize.height;
    NSArray *allNodes = [[self detailTreeController] flattenedNodes];
    NSArray *allObjects = [allNodes valueForKey:@"representedObject"];
    NSMutableArray *pageObjectsArray = [NSMutableArray array];
    NSMutableArray *currentPageObjects = [NSMutableArray array];
    CGFloat currentPageHeight = 0;
    for (id object in allObjects) {
        CGFloat currentHeight = [self printHeightForObject:object];
        if ((currentPageHeight + currentHeight) > pageHeight) {
            currentPageHeight = currentHeight;
            [pageObjectsArray addObject:currentPageObjects];
            currentPageObjects = [NSMutableArray array];
        }
        else
            currentPageHeight += currentHeight;
        [currentPageObjects addObject:object];
    }
    if ([currentPageObjects count] > 0)
        [pageObjectsArray addObject:currentPageObjects];
    range->location = 1;
    range->length = [pageObjectsArray count];
    [self setPageObjects:pageObjectsArray];
    
    NSRect printRect = NSMakeRect([printInfo leftMargin], [printInfo rightMargin], pageSize.width, pageSize.height);
    [self setFrame:printRect];

    return YES;
}

-(NSRect)rectForPage:(NSInteger)page {
    [self setCurrentPage:page - 1];
    return [self frame];
}

-(CGFloat)printHeightForObject:(id)object {
    if ([object isKindOfClass:[RNDetailHeader class]]) 
        return 0.0f;
    if ([object isKindOfClass:[RNCategory class]])
        return [RNCategory showsChildren] ? listContainerHeight : genericHeight;
    if ([object isKindOfClass:[RNAisle class]])
        return [RNAisle showsChildren] ? listContainerHeight : genericHeight;
    if ([object isKindOfClass:[RNListItem class]] && [(RNListItem *)object notes])
        return itemWithNotesHeight;
    return genericHeight;
}

@end
